﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Scala.Binding;
using Microsoft.Scripting.Ast;
using Microsoft.Scripting;

namespace Scala.Compiler.Ast
{
    using Ast = Microsoft.Scripting.Ast.Expression;
    using AstUtils = Microsoft.Scripting.Ast.Utils;

    public abstract class Tree:Ast
    {
        private ScalaAst _globalParent;
        private ScalaScope _scope;
        private Tree _parent;
        private IndexSpan _span;
        private string _typeName;
        
        internal static readonly BlockExpression EmptyBlock = Ast.Block(Ast.Empty());

        public ScalaScope Scope { get { return _scope; } set { _scope = value; } }
        public Tree Parent { get { return _parent; } set { _parent = value; } }
        public ScalaAst GlobalParent { get { return _globalParent; } }
        public IndexSpan IndexSpan { get { return _span; } set { _span = value; } }
        public string TypeName { get { return _typeName; } set { _typeName = value; } }

        public void SetLoc(ScalaAst globalParent, int start, int end)
        {
            _globalParent = globalParent;
            _span = new IndexSpan(start, end > start ? end - start : start);
        }

        public void SetLoc(ScalaAst globalParent, IndexSpan span)
        {
            _globalParent = globalParent;
            _span = span;
        }

        public SourceLocation Start
        {
            get { return GlobalParent.IndexToLocation(StartIndex); }
        }

        public SourceLocation End
        {
            get { return GlobalParent.IndexToLocation(EndIndex); }
        }

        public int EndIndex
        {
            get { return _span.End; }
            set { _span = new IndexSpan(_span.Start, value - _span.Start); }
        }

        public int StartIndex
        {
            get { return _span.Start; }
            set { _span = new IndexSpan(value, 0); }
        }

        internal SourceLocation IndexToLocation(int index)
        {
            if (index == -1)
            {
                return SourceLocation.Invalid;
            }
            var locs = GlobalParent._lineLocations;
            int match = Array.BinarySearch(locs, index);
            if (match < 0)
            {
                if (match == -1)
                {
                    return new SourceLocation(index, 1, index + 1);
                }
                match = ~match - 1;
            }
            return new SourceLocation(index, match + 2, index - locs[match] + 1);
        }

        public abstract void Walk(ScalaWalker walker);

        public abstract Ast Reduce();
    }
}
